package com.yami.shop.api.controller;

import com.yami.shop.api.config.Constants;
import com.yami.shop.api.config.PayUtil;
import com.yami.shop.bean.enums.OrderStatus;
import com.yami.shop.bean.model.Order;
import com.yami.shop.bean.model.OrderItem;
import com.yami.shop.bean.model.OrderRefund;
import com.yami.shop.bean.model.OrderSettlement;
import com.yami.shop.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;


import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.*;

import static cn.hutool.core.util.ClassUtil.getClassLoader;

@RestController
@RequestMapping("/p/refund")
@Api(tags = "退款接口")
public class RefundController  {
    private  int socketTimeout = 10000;// 连接超时时间，默认10秒
    private  int connectTimeout = 30000;// 传输超时时间，默认30秒
    private  static RequestConfig requestConfig;// 请求器的配置
    private  static CloseableHttpClient httpClient;// HTTP请求器
    @Autowired
    private OrderSettlementService orderSettlementService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private ProductService productService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private OrderRefundService orderRefundService;

    @PostMapping("/approvalRefuse")
    @ApiOperation(value = "根据订单号进行退款", notes = "此处为商户处理退款拒绝的相关业务,不需要调用微信的退款接口")
    public void approvalRefuse(){
        //此处为商户处理退款拒绝的相关业务,不需要调用微信的退款接口
    }

    @PostMapping("/updateRefundStatus")
    @ApiOperation(value = "根据订单号进行退款", notes = "微信退款服务成功后回调，修改相关业务表的退款状态,订单状态等口")
    public void updateRefundStatus(){
        //微信退款服务成功后回调，修改相关业务表的退款状态,订单状态等
    }

    /**
     * 退款
     */
    @GetMapping("/refund")
    @ApiImplicitParams({
    })
    public ResponseEntity<Map> refund(Order orderValue)  {
        ResponseEntity<Map> map = null;
        Order order = orderService.getOrderByOrderNumber(orderValue.getOrderNumber());
        /**
         * 当未发货时可直接退款
         */
        int state=order.getStatus();
        if(state==888){
            try {
                //当退款时，返利状态改为返现失败
                map = dorefund(order.getOrderNumber());
                /**
                 * 直接退款时将退款状态(refund_state)改为2（退款成功）
                 */
                orderService.updateRefund(orderValue,2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{
            HashMap hashMap = new HashMap();
            hashMap.put("code",Constants.CODE_EXAMINE);//申请审核状态
            map= ResponseEntity.ok(hashMap);
            Order  orderParam1 = orderService.getById(order.getOrderId());
            orderParam1.setOrderId(order.getOrderId());
            orderParam1.setDvyTime(new Date());
            /**
             * 退款状态（refund_state）改为申请退款 1
             */
            orderParam1.setRefundState(1);
            /**
             * 申请退款时订单状态改为取消订单
             */
            orderParam1.setOrderState(1);
            orderParam1.setUserId(order.getUserId());
            orderParam1.setRefundReason(orderValue.getRefundReason());
            orderParam1.setRefundExplain(orderValue.getRefundExplain());
            orderParam1.setRefundImg(orderValue.getRefundImg());
            orderParam1.setPhone(orderValue.getPhone());
            orderParam1.setOrderNumber(orderValue.getOrderNumber());
            orderParam1.setShopId(orderValue.getShopId());
            /**
             * 已发货时需要后台审核申请
             */
            orderService.refund(orderParam1);
        }
        List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderValue.getOrderNumber());
        for (OrderItem orderItem : orderItems) {
            productService.removeProductCacheByProdId(orderItem.getProdId());
            skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId());
        }
        return map;
    }

    //具体的调用微信的退款接口
    @GetMapping("/dorefund")
    @ApiOperation(value = "退款方法", notes = "根据订单金额退款")
    @ApiImplicitParams({
    })
    public  ResponseEntity<Map> dorefund(String orderNumber) throws Exception{
        String code = Constants.CODE_SUCCESS;//状态码
        String msg = Constants.REFUND_SUCCESS;//提示信息
        Map <String,String> data = new HashMap<String,String>();
        String  orderId = "";

        try {
                    OrderSettlement settlement = orderSettlementService.getSettlementByOrderId(orderNumber);
                    //退款到用户微信
                    String nonce_str = getRandomStringByLength(32);
                    //data.put("userId", "7c7c3c98526646c3b07d7928d9f25399");
                    data.put("appid", Constants.appID);
                    data.put("mch_id", Constants.MCH_ID);
                    data.put("nonce_str", nonce_str);
                    data.put("sign_type", "MD5");
                    data.put("out_trade_no", settlement.getPayNo());//商户订单号
                    data.put("out_refund_no",  settlement.getPayNo());//商户退款单号
                    //Math.round(userecord.getMoney() * 100);
                    data.put("total_fee",String.valueOf(Math.round(settlement.getPayAmount()*100)));//支付金额，微信支付提交的金额是不能带小数点的，且是以分为单位,这边需要转成字符串类型，否则后面的签名会失败
                    data.put("refund_fee", String.valueOf(Math.round(settlement.getPayAmount()*100)));//退款总金额,订单总金额,单位为分,只能为整数
//                data.put("notify_url", Constants.NOTIFY_URL_REFUND);//退款成功后的回调地址
                    String preStr = PayUtil.createLinkString(data); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
                    //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
                    String mySign = PayUtil.sign(preStr, Constants.key, "utf-8").toUpperCase();
                    data.put("sign", mySign);

                    //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
                    String pjxml = PayUtil.GetMapToXML(data);
                    System.out.println(pjxml);
                    String xmlStr = postData(Constants.REFUND_PATH, PayUtil.GetMapToXML(data)); //支付结果通知的xml格式数据
                    System.out.println(xmlStr);
                    Map notifyMap = PayUtil.doXMLParse(xmlStr);
                    if ("SUCCESS".equals(notifyMap.get("return_code"))) {
                        if("SUCCESS".equals(notifyMap.get("result_code"))) {
                            //退款成功的操作
                            String prepay_id = (String) notifyMap.get("prepay_id");//返回的预付单信息
                            System.out.println(prepay_id);
                            Long timeStamp = System.currentTimeMillis() / 1000;
                            //拼接签名需要的参数
                            String stringSignTemp = "appId=" + Constants.appID + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
                            //签名算法生成签名
                            String paySign = PayUtil.sign(stringSignTemp, Constants.key, "utf-8").toUpperCase();
                            data.put("package", "prepay_id=" + prepay_id);
                            data.put("timeStamp", String.valueOf(timeStamp));
                            data.put("paySign", paySign);
                        }else{
                            System.out.println("退款失败:原因"+notifyMap.get("return_msg"));
                            code = Constants.CODE_ERROR;
                            msg = (String)notifyMap.get("return_msg");
                        }
                    }else{
                        System.out.println("退款失败:原因"+notifyMap.get("return_msg"));
                        code = Constants.CODE_ERROR;
                        msg = (String)notifyMap.get("return_msg");
                    }


        }catch (Exception e) {
            code = Constants.CODE_ERROR;
            msg = Constants.MSG_01;
        }
        Map <String,Object> jsonResult = new HashMap<String,Object>();
        jsonResult.put("code",code);
        jsonResult.put("msg",msg);
        jsonResult.put("data",data);
        return  ResponseEntity.ok(jsonResult);
    }


    /**
     * 企业转账给个人
     *
     */
    @GetMapping("/transfer")
    @ApiOperation(value = "转账方法", notes = "根据商家余额给商家转账")
    @ApiImplicitParams({
    })
    public void transfer() throws Exception{
        String code = Constants.CODE_SUCCESS;//状态码
        String msg = Constants.REFUND_SUCCESS;//提示信息
        Map <String,String> data = new HashMap<String,String>();
        try {
            //退款到用户微信
            String nonce_str = getRandomStringByLength(32);
            data.put("appid", Constants.appID);
            data.put("mch_id", Constants.MCH_ID);
            data.put("nonce_str", nonce_str);
            data.put("sign_type", "MD5");
            data.put("partner_trade_no", "0000001");//商户订单号
            data.put("openid","oWG8S5R2RafYyu2_VOd71NTFosWY");
            data.put("check_name", "NO_CHECK"); //校验用户姓名选项 OPTION_CHECK
            data.put("amount","0.1");//转账金额
            data.put("desc", "商家提现"); //企业付款描述信息
            String preStr = PayUtil.createLinkString(data); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
            //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
            String mySign = PayUtil.sign(preStr, Constants.key, "utf-8").toUpperCase();
            data.put("sign", mySign);
            //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
            String pjxml = PayUtil.GetMapToXML(data);
            System.out.println(pjxml);
            String xmlStr = postData(Constants.TRANSFER_PATH, PayUtil.GetMapToXML(data)); //支付结果通知的xml格式数据
            System.out.println(xmlStr);
            Map notifyMap = PayUtil.doXMLParse(xmlStr);
        }catch (Exception e) {
            code = Constants.CODE_ERROR;
            msg = Constants.MSG_01;
        }
    }

    /**
     * 加载证书
     *
     */
    @PostMapping("/initCert")
    @ApiOperation(value = "加载证书", notes = "根据文件路径加载证书")
    private static void initCert() throws Exception {
        // 证书密码，默认为商户ID
        String key = Constants.MCH_ID;
        // 商户证书的路径
        //String path = Constants.CERT_PATH;
        //String filePath = RefundController.class.getClassLoader().getResource("apiclient_cert.p12").getFile();
        InputStream instream=RefundController.class.getResourceAsStream("/apiclient_cert.p12");
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");

        // 读取本机存放的PKCS12证书文件
        //FileInputStream instream = new FileInputStream(new File(filePath));
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();

        // 指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        // 设置httpclient的SSLSocketFactory
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }


    /**
     * 通过Https往API post xml数据
     * @param url  API地址
     * @param xmlObj   要提交的XML数据对象
     * @return
     */
    @PostMapping("/postData")
    @ApiOperation(value = "合成xml对象", notes = "根据参数生成xml对象")
    public  String postData(String url, String xmlObj) {
        // 加载证书
        try {
            initCert();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
            }  catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            }  catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }

    @PostMapping("/getRandomStringByLength")
    @ApiOperation(value = "生成随机数对象", notes = "生成随机数对象")
    private  String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
